﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Reflection;
using Metaspec;

// This example show using of extern aliases.

namespace AliasExample
{
	class Program
	{
		static void Main(string[] args)
		{
			//creating project1
			ICsProject project1 = ICsProjectFactory.create(project_namespace.pn_project_namespace);
			project1.setBuildEntityModel(true);

			{
				//adding assemblies
				string assembly_path = Assembly.GetAssembly(typeof(object)).Location;
				byte[] assembly_buffer = File.ReadAllBytes(assembly_path);
				IExternalAssemblyModule module = IExternalAssemblyModuleFactory.create(assembly_buffer, assembly_path);
				project1.addExternalAssemblyModules(new IExternalAssemblyModule[] { module }, false, null);

				//adding C# file
				string directory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), @"..\..");
				string path = Path.Combine(directory, "Project1.cs");
				char[] buffer = File.ReadAllText(path).ToCharArray();
				ICsFile file = ICsFileFactory.create(buffer, path);
				project1.addFiles(file);
			}

			//creating project2
			ICsProject project2 = ICsProjectFactory.create(project_namespace.pn_project_namespace);
			project2.setBuildEntityModel(true);

			IExternalAssembly assembly;
			{
				//adding assemblies
				{
					string assembly_path = Assembly.GetAssembly(typeof(object)).Location;
					byte[] assembly_buffer = File.ReadAllBytes(assembly_path);
					IExternalAssemblyModule module = IExternalAssemblyModuleFactory.create(assembly_buffer, assembly_path);
					project2.addExternalAssemblyModules(new IExternalAssemblyModule[] { module }, false, null);
				}
				{
					string directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
					string assembly_path = Path.Combine(directory, "a.dll");
					byte[] assembly_buffer = File.ReadAllBytes(assembly_path);
					IExternalAssemblyModule module = IExternalAssemblyModuleFactory.create(assembly_buffer, assembly_path);
					project2.addExternalAssemblyModules(new IExternalAssemblyModule[] { module }, true, null);
					assembly = module.getExternalAssembly();
				}

				//adding C# file
				{
					string directory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), @"..\..");
					string path = Path.Combine(directory, "Project2.cs");
					char[] buffer = File.ReadAllText(path).ToCharArray();
					ICsFile file = ICsFileFactory.create(buffer, path);
					project2.addFiles(file);
				}

				//adding reference to project1
				project2.addProjectReferences(new ProjectReference(project1, "alias"));
				//adding reference to assembly with alias
				project2.addAssemblyReferences(new ExternalAssemblyReference(assembly, "a"));
			}

			//creating solution
			ISolution solution = ISolutionFactory.create();
			solution.addProjects(project1);
			solution.addProjects(project2);

			//parsing
			solution.parse();

			//checking project alias
			{
				CsCompilationUnit unit = project2.getFiles()[0].getCompilationUnit();
				CsNamespace ns = (CsNamespace)unit.declarations[0];
				CsClass c = (CsClass)ns.member_declarations[0];
				CsMethod m = (CsMethod)c.member_declarations[0];
				CsBlock body = m.definition;
				CsLocalVariableDeclaration decl = (CsLocalVariableDeclaration)((CsDeclarationStatement)body.statements[0]).declaration;
				CsEntity entity = getEntity(project1.getNamespace(), "Project1", "C");
				Debug.Assert(decl.type.entity_typeref.type == cs_entity_type.et_class);
				Debug.Assert(decl.type.entity_typeref.u == entity);
			}
			//checking assembly alias
			{
				CsCompilationUnit unit = project2.getFiles()[0].getCompilationUnit();
				CsNamespace ns = (CsNamespace)unit.declarations[0];
				CsClass c = (CsClass)ns.member_declarations[0];
				CsMethod m = (CsMethod)c.member_declarations[0];
				CsBlock body = m.definition;
				CsLocalVariableDeclaration decl = (CsLocalVariableDeclaration)((CsDeclarationStatement)body.statements[2]).declaration;
				CsEntity entity = getEntity(project2.getNamespace(), "A", "C");
				Debug.Assert(decl.type.entity_typeref.type == cs_entity_type.et_class);
				Debug.Assert(decl.type.entity_typeref.u == entity);
				Debug.Assert(((CsEntity)decl.type.entity_typeref.u).assembly == assembly.getAssemblyEntity());
			}
		}

		static CsEntity getEntity(CsScopeEntity scope, params string[] names)
		{
			CsEntity entity = null;
			for (int i = 0; i != names.Length; ++i )
			{
				entity = scope.scope_table.get_single_id(names[i]);
				if (i != names.Length - 1)
				{
					scope = (CsScopeEntity)entity;
				}
			}
			return entity;
		}
	}
}
